/**
 * 
 */
package it.polimi.cg16.client;

import it.polimi.cg16.model.Color;
import it.polimi.cg16.model.Sheep;
import it.polimi.cg16.model.Terrain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Michele
 * 
 */
public class ClientController {
    protected static final int CLOSING_NOTICE = -1;

    protected PlayerClient player;
    protected GameBoardClient gameBoard;
    protected boolean isUpdate;

    protected NetAdapter netAdapter;
    protected String message;

    protected List<PlayerClient> otherPlayers;
    protected Map<Terrain, Integer> sheepsPerTerrain;

    public ClientController(NetAdapter netAdapter) {
        this.netAdapter = netAdapter;
        gameBoard = new GameBoardClient();
        player = new PlayerClient(this.netAdapter.getUsername());

        otherPlayers = new ArrayList<>();
        sheepsPerTerrain = new HashMap<>();

        isUpdate = false;
    }

    /**
     * Close all the connection, sending a closing notice to the server and exit
     */
    public void close() {
        netAdapter.sendInt(CLOSING_NOTICE);
        netAdapter.closeConnection();
        System.exit(0);
    }

    /**
     * Receive an object from server
     */
    public void receiveObject() {
        message = netAdapter.readString();
        if ("#update#".equals(message)) {
            updateClient();
        } else if ("#initialUpdate#".equals(message)) {
            initialUpdateClient();
        } else if ("#chooseMove#".equals(message)) {
            chooseMoveClient();
        } else if ("#chooseSheep#".equals(message)) {
            chooseSheepClient();
        } else if ("#chooseCell#".equals(message)) {
            chooseCellClient();
        } else if ("#chooseCard#".equals(message)) {
            chooseCardClient();
        } else if ("#chooseShepherd#".equals(message)) {
            chooseShepherd();
        } else if ("#endGame#".equals(message)) {
            showResultsClient();
        } else if ("#endTurn#".equals(message)) {
            endTurn();
        } else {

            // throw new ClassNotFoundException();
        }
    }

    /**
     * Performs start turn action
     */
    public void startTurn() {
        // Implemented in child
    }

    /**
     * Performs end turn action
     */
    public void endTurn() {
        // Implemented in child
    }

    /**
     * Show the result to the player
     */
    public void showResultsClient() {
        int pcSize = netAdapter.readInt();
        for (int i = 0; i < pcSize; i++) {
            String username = netAdapter.readString();
            otherPlayers.add(new PlayerClient(username));
            otherPlayers.get(i).setMoney(netAdapter.readInt());
            int cSize = netAdapter.readInt();
            List<Card> cards;
            cards = new ArrayList<>();
            for (int j = 0; j < cSize; j++) {
                int constant = netAdapter.readInt();
                int number = netAdapter.readInt();
                for (Terrain t : Terrain.values()) {
                    if (t.getTypeConstant() == constant) {
                        cards.add(new Card(t, number));
                    }
                }
            }
            otherPlayers.get(i).setTerrainCards(cards);
            int colorId = netAdapter.readInt();
            Color color = null;
            for (Color col : Color.values()) {
                if (col.getId() == colorId) {
                    color = col;
                }
            }
            otherPlayers.get(i).setColor(color);

        }
        for (Terrain t : Terrain.values()) {
            int sheeps = 0;
            for (int i = 1; i < gameBoard.getRegions().length; i++) {
                if (gameBoard.getRegions()[i].getTerrain().equals(t)) {
                    sheeps = sheeps + gameBoard.getRegions()[i].getSheep().size();
                }
            }
            if (gameBoard.getBlackSheepIdRegion() != 0 && t.equals(gameBoard.getRegions()[gameBoard.getBlackSheepIdRegion()].getTerrain())) {
                sheeps++;
            }
            sheepsPerTerrain.put(t, sheeps);
        }
    }

    /**
     * Asks which shepherd want to use at the beginning of the turn
     */
    public void chooseShepherd() {

        // Implemented in child
    }

    /**
     * Method to receive and show buyable terrain card in the gameboard
     */
    public void chooseCardClient() {
        // Implemented in child
    }

    /**
     * Method to show free cell in the gameboard
     */
    public void chooseCellClient() {
        // Implemented in child
    }

    /**
     * Method to show selectable sheep in a region near shepherd
     */
    public void chooseSheepClient() {
        // Implemented in child
    }

    /**
     * Method to show available moves in the gameboard
     */
    public void chooseMoveClient() {
        // Implemented in child
    }

    /**
     * Method to update client data initially
     */
    public void initialUpdateClient() {
        updateCellsClient();
        updateRegionsClient();
        updateDeckClient();
        updateMoneyClient();
        updateTerrainCardsClient();
        updateWolf();
    }

    /**
     * Method to update client data
     */
    public void updateClient() {
        updateCellsClient();
        updateRegionsClient();
        updateBlackSheep();
        updateDeckClient();
        updateMoneyClient();
        updateTerrainCardsClient();
        updateShepherdsClient();
        updateOtherShepherds();
        updateWolf();
    }

    /**
     * Method to update client cells
     */
    public void updateCellsClient() {
        if ("#updateCells#".equals(netAdapter.readString())) {
            int length = netAdapter.readInt();
            for (int i = 0; i < length; i++) {
                int idCell = netAdapter.readInt();
                int state = netAdapter.readInt();
                gameBoard.getCells()[idCell].setState((short) state);
            }
        }
    }

    /**
     * Method to update client regions
     */
    public void updateRegionsClient() {
        if ("#updateRegions#".equals(netAdapter.readString())) {
            int length = netAdapter.readInt();
            for (int i = 0; i < length; i++) {
                int idRegion = netAdapter.readInt();
                int lengthSheeps = netAdapter.readInt();
                List<Sheep> sheeps;
                sheeps = new ArrayList<Sheep>();
                for (int j = 0; j < lengthSheeps; j++) {
                    int id = netAdapter.readInt();
                    Sheep s = new Sheep(id, gameBoard.getRegions()[idRegion]);
                    sheeps.add(s);
                }
                gameBoard.getRegions()[idRegion].setSheeps(sheeps);
            }
        }
    }

    /**
     * Method to update region of the blackSheep
     */
    public void updateBlackSheep() {
        if ("#updateBlackSheep#".equals(netAdapter.readString())) {
            int idRegion = netAdapter.readInt();
            gameBoard.setBlackSheep(idRegion);
            int killed = netAdapter.readInt();
            if (killed == 0) {
                gameBoard.getBlackSheep().setKilled(false);
            } else {
                gameBoard.getBlackSheep().setKilled(true);
            }
        }
    }

    private void updateWolf() {
        if ("#updateWolf#".equals(netAdapter.readString())) {
            int idRegion = netAdapter.readInt();
            gameBoard.setWolf(idRegion);
        }
    }

    /**
     * Method to update client deck
     */
    public void updateDeckClient() {
        if ("#updateDeck#".equals(netAdapter.readString())) {
            int length = netAdapter.readInt();
            gameBoard.getDeckClient().clear();
            for (int i = 0; i < length; i++) {
                int constant = netAdapter.readInt();
                int cost = netAdapter.readInt();
                for (Terrain t : Terrain.values()) {
                    if (t.getTypeConstant() == constant) {
                        gameBoard.getDeckClient().add(new Card(t, cost));
                    }
                }
            }
        }

    }

    /**
     * Method to update client money
     */
    public void updateMoneyClient() {
        if ("#updateMoney#".equals(netAdapter.readString())) {
            int money = netAdapter.readInt();
            player.setMoney(money);
        }

    }

    /**
     * Method to update client player's terrain cards
     */
    public void updateTerrainCardsClient() {
        if ("#updateTerrainCards#".equals(netAdapter.readString())) {
            int length = netAdapter.readInt();
            player.getTerrainCards().clear();
            for (int i = 0; i < length; i++) {
                int constant = netAdapter.readInt();
                int cost = netAdapter.readInt();
                for (Terrain t : Terrain.values()) {
                    if (t.getTypeConstant() == constant) {
                        player.getTerrainCards().add(new Card(t, cost));
                    }
                }
            }
        }

    }

    /**
     * Method to update client shepherds
     */
    public void updateShepherdsClient() {
        if ("#updateShepherd#".equals(netAdapter.readString())) {
            int length = netAdapter.readInt();
            for (int i = 0; i < length; i++) {
                int cellId = netAdapter.readInt();
                int colorId = netAdapter.readInt();
                Color color = null;
                for (Color col : Color.values()) {
                    if (col.getId() == colorId) {
                        color = col;
                    }
                }
                CellClient[] cells = gameBoard.getCells();
                for (CellClient c : cells) {
                    if (c.getId() == cellId) {
                        if (isUpdate) {
                            player.getShepherds().get(i).setCell(c);
                            player.getShepherds().get(i).setColor(color);
                        } else {
                            player.getShepherds().add(new ShepherdClient(i, c, color));
                        }
                    }
                }
            }
            player.setChosenShepherd(netAdapter.readInt());
        }
    }

    /**
     * Method to update all other shepherd in the gameboard
     */
    public void updateOtherShepherds() {
        if ("#updateOtherShepherd#".equals(netAdapter.readString())) {
            int length = netAdapter.readInt();
            for (int i = 0; i < length; i++) {
                int cellId = netAdapter.readInt();
                int colorId = netAdapter.readInt();
                Color color = null;
                for (Color col : Color.values()) {
                    if (col.getId() == colorId) {
                        color = col;
                    }
                }
                CellClient[] cells = gameBoard.getCells();
                for (CellClient c : cells) {
                    if (c.getId() == cellId) {
                        if (isUpdate) {
                            gameBoard.getOtherShepherds().get(i).setCell(c);
                            gameBoard.getOtherShepherds().get(i).setColor(color);
                        } else {
                            gameBoard.getOtherShepherds().add(new ShepherdClient(c, color));
                        }
                    }
                }
            }
        }
    }
}
